/*
Simple Forum Auto Poster.
Copyright (C) 2011 Gianni Van Hoecke.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package persistence.net;

import domain.net.LinkChecker;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.List;
import java.util.zip.GZIPInputStream;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;

/**
 *
 * @author Gianni Van Hoecke <gianni.vh@gmail.com>
 *
 * Handles the communication with the internet.
 */
public class Client
{
    private HttpClient client;
    private HttpContext context;
    private HttpPost post;
    private HttpGet get;
    private HttpResponse response;
    private Header[] responseHeaders;
    private CookieStore cookieStore;

    private String responseContent;
    private String responseURL;
    private String statusLine;
    private String lastURL;

    /**
     * Default constructor. Creates a new Http client.
     */
    public Client()
    {
        responseContent = "";
        responseURL = "";
        statusLine = "";
        lastURL = "";
        responseHeaders = null;
        client = new DefaultHttpClient();
        cookieStore = new BasicCookieStore();
        context = new BasicHttpContext();
        context.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
        client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
    }

    /**
     * Does a POST to the given URL.
     * @param url The URL to post to.
     * @param nameValuePairs The value pairs to post.
     * @throws Exception on error.
     */
    public void doPost(String url, List<NameValuePair> nameValuePairs) throws Exception
    {
        this.lastURL = url;
        post = new HttpPost(url);
        post.setHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1");
        post.setHeader("Content-Type", "application/x-www-form-urlencoded");
        post.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        post.setHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
        post.setHeader("Connection", "keep-alive");
        
        post.setEntity(new UrlEncodedFormEntity(nameValuePairs));

        response = client.execute(post, context);

        this.parseResponse();
    }

    /**
     * Does a GET of the given URL.
     * @param url The URL to open.
     * @throws Exception on error.
     */
    public void doGet(String url) throws Exception
    {
        this.lastURL = url;
        get = new HttpGet(url);
        get.setHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1");
        response = client.execute(get, context);
        this.parseResponse();
    }

    /**
     * Does a POST to the given URL.
     * @param fileValuePair The file to upload. null for just text.
     * @param nameValuePairs The value pairs to post
     * @param url The url to post to.
     */
    public void doPostUploadFile(String url, List<NameValuePair> nameValuePairs, FileValuePair fileValuePair) throws Exception
    {
        this.lastURL = url;
        String boundary = "---------------------------168072824752491622650073";
        post = new HttpPost(url);

        final HttpParams params = new BasicHttpParams();
        params.setParameter("http.protocol.handle-redirects", true);
        HttpClientParams.setRedirecting(params, true);
        post.setParams(params);
        System.out.println("wat is dit ");
        post.setHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1");
        post.setHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
        post.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        post.setHeader("Accept-Encoding", "gzip, deflate");
        post.setHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
        post.setHeader("Keep-Alive", "115");
        post.setHeader("Connection", "keep-alive");
        post.setHeader("Referer", url);
        
        MultipartEntity mpEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE,
                                                           boundary,
                                                           Charset.forName("UTF-8"));

        if(fileValuePair != null)
        {
            FileBody fileBody = new FileBody(fileValuePair.getValue(), fileValuePair.getValue().getName(), "image/jpeg", "UTF-8");
            mpEntity.addPart(fileValuePair.getFieldName(), fileBody);
        }
        
        for(NameValuePair n : nameValuePairs)
        {
            mpEntity.addPart(n.getName(), new StringBody(n.getValue(), "text/plain", Charset.forName("UTF-8")));
        }

        post.setEntity(mpEntity);

        /*
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        mpEntity.writeTo(os);
        try
        {
            System.out.println(new String(os.toByteArray(), "UTF-8"));
            
        } catch (Exception e) {
           
        }
        */
        
        response = client.execute(post, context);

        try
        {
            if(response.getEntity().getContentEncoding().toString().toLowerCase().trim().endsWith("gzip"))
            {
                this.parseResponseGZIP();
            }
            else
            {
                this.parseResponse();
            }
        }
        catch(Exception e)
        {
            this.parseResponse();
        }
    }

    /**
     * Gets and extracts the GZIP response after a POST or GET.
     * @throws Exception
     */
    private void parseResponseGZIP() throws Exception
    {
        //CONTENT
        this.responseHeaders = response.getAllHeaders();
        this.statusLine = response.getStatusLine().toString();
        this.responseContent = "";
        BufferedReader rd = new BufferedReader(new InputStreamReader(new GZIPInputStream(response.getEntity().getContent())));
        String line = "";
        while ((line = rd.readLine()) != null)
        {
            this.responseContent += line;
        }
        //URL
        HttpUriRequest currentReq = (HttpUriRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
        HttpHost currentHost = (HttpHost)  context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
        try
        {
            responseURL = currentHost.toURI() + currentReq.getURI();
        }catch(Exception e){/*ignore*/}

        //Check for location:xxx
        for(Header header : this.responseHeaders)
        {
            if(header.getName().toLowerCase().equals("location"))
            {
                this.doGet(LinkChecker.checkString(this.lastURL, header.getValue()));
                break;
            }
        }
    }

    /**
     * Gets the response after a POST or GET.
     * @throws Exception
     */
    private void parseResponse() throws Exception
    {
        //CONTENT
        this.responseHeaders = response.getAllHeaders();
        this.statusLine = response.getStatusLine().toString();
        this.responseContent = "";
        BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        String line = "";
        while ((line = rd.readLine()) != null)
        {
            this.responseContent += line;
        }
        //URL
        HttpUriRequest currentReq = (HttpUriRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
        HttpHost currentHost = (HttpHost)  context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
        try
        {
            responseURL = currentHost.toURI() + currentReq.getURI();
        }catch(Exception e){/*ignore*/}

        //Check for location:xxx
        for(Header header : this.responseHeaders)
        {
            if(header.getName().toLowerCase().equals("location"))
            {
                this.doGet(LinkChecker.checkString(this.lastURL, header.getValue()));
                break;
            }
        }
    }

    /*
     *
     * GETTERS AND SETTERS
     * 
     */

    /**
     * @return The current HTTP client.
     */
    public HttpClient getClient()
    {
        return client;
    }

    /**
     * @return The HttpResponse object.
     */
    public HttpResponse getResponse()
    {
        return response;
    }

    /**
     * @return The HTML that was sent to the client after a POST or GET.
     */
    public String getResponseContent()
    {
        return responseContent;
    }

    /**
     * This method cannot be trusted. Don't rely on it.
     * @return The URL after a POST or GET.
     */
    public String getResponseURL()
    {
        return responseURL;
    }

    /**
     * @return The header that were sent to the client after a POST or GET.
     */
    public Header[] getResponseHeaders()
    {
        return responseHeaders;
    }

    /**
     * @return The status line after a POST or GET.
     */
    public String getStatusLine()
    {
        return statusLine;
    }
}